#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <memory>
#include <jsoncpp/json/json.h>
#include <filesystem>

#include "log.hpp"
#include "bundle.h"
#include "Error.hpp"

namespace cloud
{

    namespace fs = std::filesystem;

    // 文件属性操作类
    class FileUtil
    {
    public:
        // 构造函数
        FileUtil(const std::string &name)
            : _filename(name)
        {
        }

        // 获取文件大小
        int64_t FileSize()
        {
            struct stat st;
            int n = stat(_filename.c_str(), &st);
            if (n < 0)
            {
                lg(ERROR, "Stat failure, errno: %d ,error: %s", errno, strerror(errno));
                return STATERROR;
            }
            return st.st_size;
        }

        // 最近访问时间
        time_t LastAtime()
        {
            struct stat st;
            int n = stat(_filename.c_str(), &st);
            if (n < 0)
            {
                lg(ERROR, "Stat failure, errno: %d ,error: %s", errno, strerror(errno));
                return STATERROR;
            }
            return st.st_atime;
        }

        // 最近修改时间
        time_t LastMtime()
        {
            struct stat st;
            int n = stat(_filename.c_str(), &st);
            if (n < 0)
            {
                lg(ERROR, "Stat failure, errno: %d ,error: %s", errno, strerror(errno));
                return STATERROR;
            }
            return st.st_mtime;
        }

        // 提取文件名
        std::string Filename()
        {
            int pos = _filename.rfind("/");
            if (pos == std::string::npos)
            {
                lg(WARNING, "filename.rfind failure, errno: %d ,error: %s", errno, strerror(errno));
                return _filename;
            }
            return _filename.substr(pos + 1);
        }

        // 获取指定len长度的文本
        bool GetPosLen(std::string *body, int pos, int len)
        {
            size_t fsize = this->FileSize();
            if (len + pos > fsize)
            {
                lg(WARNING, "GetPosLen is overfloat");
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename, std::ios::in | std::ios::binary);
            if (!ifs.is_open())
            {
                lg(ERROR, "ifs open file error");
                return false;
            }
            ifs.seekg(pos, std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0], len);
            if (ifs.fail())
            {
                lg(ERROR, "read file error");
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }

        // 获取all文本
        bool GetContent(std::string *body)
        {
            size_t fsize = this->FileSize();
            return GetPosLen(body, 0, fsize);
        }

        // 写入文本
        bool SetContent(std::string &body)
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);
            if (ofs.is_open() == false)
            {
                lg(ERROR, "ofs open file error");
                return false;
            }
            ofs.write(&body[0], body.size());
            if (ofs.good() == false)
            {
                lg(ERROR, "write file error");
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }

        // 压缩
        bool Compress(const std::string &packname)
        {
            // 获取数据
            std::string body;
            if (this->GetContent(&body) == false)
            {
                lg(ERROR, "Compress gets data error");
                return false;
            }

            // 压缩数据
            std::string pack = bundle::pack(bundle::LZIP, body);

            // 写回数据
            FileUtil fu(packname);
            if (!fu.SetContent(pack))
            {
                lg(ERROR, "Compress SetContent error");
                return false;
            }
            return true;
        }

        // 解压缩
        bool UnCompress(const std::string &unpackname)
        {
            // 获取数据
            std::string body;
            if (!this->GetContent(&body))
            {
                lg(ERROR, "UnCompress gets data error");
                return false;
            }

            // 解压缩数据
            std::string unpack = bundle::unpack(body);

            // 写回数据
            FileUtil fu(unpackname);
            if (!fu.SetContent(unpack))
            {
                lg(ERROR, "UnCompress SetContent error");
                return false;
            }
            return true;
        }

        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirectory()
        {
            if (this->Exists())
                return false;
            return fs::create_directories(_filename);
            return true;
        }
        bool ScanDirectory(std::vector<std::string> *array)
        {
            for (auto &p : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p))
                    continue;
                array->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }

    private:
        std::string _filename;
    };

    // 序列化和反序列化接口 , 线程安全的
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root,std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            sw->write(root,&ss);
            *str = ss.str();
            return true;           
   
        }
        static bool UnSerialize(const std::string &str,Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(),str.c_str()+str.size(),root,&err);
            if(!ret){
                lg(ERROR,"UnSerialize parse error");
                return false;
            }
            return true;
        }
    };
};